use super::{
    err, err_value, json, ok, throw_err, Base, CBase, Client, ECode, MapSv, Resp, VBase, VMap,
    Value, VecF,
};
use std::collections::HashMap;

pub struct Xbar;

impl Base for Xbar {}

impl CBase for Xbar {}

impl Xbar {
    pub async fn do_cmd(&self, cmd: &str, para: &Value, client: &Client) -> Resp {
        let data = para.ckobj()?;
        let row = data.ck_i64("row")?;

        match cmd {
            "xbar" => self.do_xbar(data, row, client).await,
            _ => throw_err!(ECode::NoServe, "no Xbar serve"),
        }
    }

    async fn get_data(
        &self,
        data: &MapSv,
        row: i64,
        client: &Client,
    ) -> Result<(Vec<VecF>, VecF), Value> {
        let tab = data.ck_str_empty("tab")?;
        let obj = data.ck_str_empty("obj")?;

        let cmd = self.data_xbar_cmd(tab, obj, data)?;
        let body = client.timedb(&cmd, "get").await?;
        let body = body.ck_obj(tab)?.ck_array("data")?;

        let mut data_main = Vec::new();

        for d in body.iter() {
            if let Some(v) = d.k_f64(obj) {
                data_main.push(v);
            }
        }

        let len = data_main.len();
        if (row <= 2 && len < 100)
            || (row == 3 && len < 80)
            || ((row == 4 || row == 5) && len < 70)
            || (row >= 6 && len < 60)
        {
            return Err(json!({
                "code": 3,
                "err": "no enough data"
            }));
        }

        let mut data_table: Vec<VecF> = Vec::new();

        for it in data_main.chunks(row as usize) {
            let mut tmp = VecF::new();
            for d in it.iter() {
                tmp.push(*d);
            }
            data_table.push(tmp);
        }

        Ok((data_table, data_main))
    }

    async fn do_xbar(&self, data: &MapSv, row: i64, client: &Client) -> Resp {
        let (data_table, mut data_main) = self.get_data(data, row, client).await?;

        let total = data_main.len();

        // 每列极差(y轴)
        let mut r = VecF::new();
        // 每列平均值(y轴)
        let mut x = VecF::new();
        // 每列标准差(y轴)
        let mut s = VecF::new();

        let mut r_mean = 0.0;
        let mut s_mean = 0.0;
        let mut x_mean = 0.0;

        let mut sp = 0.0;
        let mut sn = 0.0;

        for d in data_table.iter() {
            let mut it = d.iter();
            let f = it.next().ok_or("no data table")?;

            let mut min = *f;
            let mut max = *f;
            let mut sum = *f;
            let mut var = 0.0;

            for t in it {
                min = min.min(*t);
                max = max.max(*t);
                sum += *t;
            }

            let n = d.len() as f64;

            let mean = sum / n;

            for t in d.iter() {
                let tmp = t - mean;
                var += tmp.powf(2.0);
            }

            sp += var;
            sn += n - 1.0;

            let std = (var / (n + 1.0)).sqrt();

            let rv = max - min;

            r_mean += rv;
            s_mean += std;
            x_mean += mean;

            r.push(rv);
            x.push(mean);
            s.push(std);
        }

        r_mean /= r.len() as f64;
        s_mean /= s.len() as f64;
        x_mean /= x.len() as f64;

        sp = (sp / sn).sqrt();

        let r_ucl = r_mean * self.get_value(row, "D4");
        let r_lcl = (r_mean * self.get_value(row, "D3")).max(0.0);

        let s_ucl = s_mean * self.get_value(row, "B4");
        let s_lcl = (s_mean * self.get_value(row, "B3")).max(0.0);

        let tmp = r_mean * self.get_value(row, "A2");
        let x_ucl = x_mean + tmp;
        let x_lcl = (x_mean - tmp).max(0.0);

        // R图
        let res_r = json!({
            "maindata": r,
            "ucl": r_ucl,
            "lcl": r_lcl,
            "cl": r_mean
        });

        // X图
        let res_x = json!({
            "maindata": x,
            "ucl": x_ucl,
            "lcl": x_lcl,
            "cl": x_mean,
        });

        // S图
        let res_s = json!({
            "maindata": s,
            "ucl": s_ucl,
            "lcl": s_lcl,
            "cl": s_mean,
        });

        let mut ret = HashMap::new();

        ret.insert("r", res_r);
        ret.insert("x", res_x);
        ret.insert("s", res_s);
        ret.insert("g", json!(data_table));
        ret.insert("total", json!(total));
        ret.insert("row", json!(row));

        if let Some((res_n, sta)) =
            self.do_normal(&mut data_main, row, data_table.len() as i64, Some(sp))
        {
            ret.insert("n", res_n);
            ret.insert("sta", json!(sta));

            if let Some(cpk) = self.calc_cpk(data, &sta) {
                ret.insert("cpk", json!(cpk));
            }
        }

        ok!(ret)
    }
}
